#include "StdAfx.hpp"
#include "Sky.hpp"
#include "Framework.hpp"
#include "D3dUtils.hpp"
#include "ProgramMain.hpp"
#include "Multishader.hpp"
#include "Texture.hpp"
#include "Camera.hpp"
#include "Renderer.hpp"


VariableManager CylinderSkyDesc::s_VarMan;

////////////////////////////////////////////////////////////////////////////////
// class SimpleSky

static enum SINGLE_COLOR_SKY_VARS {
	SINGLE_COLOR_VAR_COLOR,
};

VariableManager SingleColorSky::s_VarMan;

SingleColorSky::SingleColorSky()
: m_Color(COLORF_BLACK)
{

}

bool SingleColorSky::Render( const ParamsCamera &camera )
{
	renderer::RenderSingleColorSky(camera, m_Color);
	return true;
}

VariableManager & SingleColorSky::GetVarMan()
{
	if (!s_VarMan.IsInitialized())
	{
		s_VarMan.AddVar(new ColorfVariable("Color", SINGLE_COLOR_VAR_COLOR));
		s_VarMan.FinishInit();
	}
	return s_VarMan;
}

void SingleColorSky::GetParamValue(uint id, void *outData) const
{
	switch (id)
	{
	case SINGLE_COLOR_VAR_COLOR:
		*(COLORF*)outData = GetColor();
		break;
	default:
		assert(0);
	}
}

void SingleColorSky::SetParamValue(uint id, const void *inData)
{
	switch (id)
	{
	case SINGLE_COLOR_VAR_COLOR:
		SetColor(*(const COLORF*)inData);
		break;
	default:
		assert(0);
	}
}


////////////////////////////////////////////////////////////////////////////////
// class CylinderSky

static enum CYLINDER_SKY_VARS {
	CYLINDER_SKY_VAR_TEX_TRANSFORM_0_C,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_0_T,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_1_C,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_1_T,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_2_C,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_2_T,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_3_C,
	CYLINDER_SKY_VAR_TEX_TRANSFORM_3_T,
	CYLINDER_SKY_VAR_BLEND_MODE_1,
	CYLINDER_SKY_VAR_BLEND_MODE_2,
	CYLINDER_SKY_VAR_BLEND_MODE_3,
	CYLINDER_SKY_VAR_FOG_COLOR,
	CYLINDER_SKY_VAR_FOG_BEG_PERCENT,
	CYLINDER_SKY_VAR_FOG_END_PERCENT,
	CYLINDER_SKY_VAR_LAYER_COUNT,
	CYLINDER_SKY_VAR_TEXTURE_0,
	CYLINDER_SKY_VAR_TEXTURE_1,
	CYLINDER_SKY_VAR_TEXTURE_2,
	CYLINDER_SKY_VAR_TEXTURE_3,
	CYLINDER_SKY_VAR_COLOR0,
	CYLINDER_SKY_VAR_COLOR1,
	CYLINDER_SKY_VAR_COLOR2,
	CYLINDER_SKY_VAR_COLOR3,
};

VariableManager CylinderSky::s_VarMan;
StaticEnumType *CylinderSky::s_BlendEnumType;

static const char * SKY_CYLINDER_SHADER_MACRO_NAMES[] = {
	"LAYER1", "LAYER2", "LAYER3",
};
static const uint SKY_CYLINDER_SHADER_MACRO_BIT_COUNTS[] = {
	2, 2, 2,
};

const char * CylinderSky::BLEND_MODE_NAMES[] = {
	"Add",
	"Lerp",
	NULL
};

VariableManager & CylinderSky::GetVarMan()
{
	if (!s_VarMan.IsInitialized())
	{
		s_BlendEnumType = new StaticEnumType(BLEND_COUNT, BLEND_MODE_NAMES); // Never free.

		uint texMatrixEnableFlags = MatrixVariable::ENABLE_COL_1 | MatrixVariable::ENABLE_COL_2
			| MatrixVariable::ENABLE_ROW_1 | MatrixVariable::ENABLE_ROW_2 | MatrixVariable::ENABLE_ROW_4;

		s_VarMan.AddVar(new MatrixVariable("Tex Transform 1 C", CYLINDER_SKY_VAR_TEX_TRANSFORM_0_C, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 1 T", CYLINDER_SKY_VAR_TEX_TRANSFORM_0_T, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 2 C", CYLINDER_SKY_VAR_TEX_TRANSFORM_1_C, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 2 T", CYLINDER_SKY_VAR_TEX_TRANSFORM_1_T, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 3 C", CYLINDER_SKY_VAR_TEX_TRANSFORM_2_C, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 3 T", CYLINDER_SKY_VAR_TEX_TRANSFORM_2_T, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 4 C", CYLINDER_SKY_VAR_TEX_TRANSFORM_3_C, 0, texMatrixEnableFlags));
		s_VarMan.AddVar(new MatrixVariable("Tex Transform 4 T", CYLINDER_SKY_VAR_TEX_TRANSFORM_3_T, 0, texMatrixEnableFlags));

		s_VarMan.AddVar(new FilePathVariable("Texture 0", CYLINDER_SKY_VAR_TEXTURE_0, 0, GetTextureFileType()));
		s_VarMan.AddVar(new FilePathVariable("Texture 1", CYLINDER_SKY_VAR_TEXTURE_1, 0, GetTextureFileType()));
		s_VarMan.AddVar(new FilePathVariable("Texture 2", CYLINDER_SKY_VAR_TEXTURE_2, 0, GetTextureFileType()));
		s_VarMan.AddVar(new FilePathVariable("Texture 3", CYLINDER_SKY_VAR_TEXTURE_3, 0, GetTextureFileType()));

		s_VarMan.AddVar(new ColorfVariable("Color 0", CYLINDER_SKY_VAR_COLOR0, 0, true));
		s_VarMan.AddVar(new ColorfVariable("Color 1", CYLINDER_SKY_VAR_COLOR1, 0, true));
		s_VarMan.AddVar(new ColorfVariable("Color 2", CYLINDER_SKY_VAR_COLOR2, 0, true));
		s_VarMan.AddVar(new ColorfVariable("Color 3", CYLINDER_SKY_VAR_COLOR3, 0, true));

		s_VarMan.AddVar(new EnumVariable("Blend Mode 1", CYLINDER_SKY_VAR_BLEND_MODE_1, 0, s_BlendEnumType));
		s_VarMan.AddVar(new EnumVariable("Blend Mode 2", CYLINDER_SKY_VAR_BLEND_MODE_2, 0, s_BlendEnumType));
		s_VarMan.AddVar(new EnumVariable("Blend Mode 3", CYLINDER_SKY_VAR_BLEND_MODE_3, 0, s_BlendEnumType));
		
		s_VarMan.AddVar(new ColorfVariable("Fog Color", CYLINDER_SKY_VAR_FOG_COLOR));
		s_VarMan.AddVar(new FloatVariable("Fog Beg Percent", CYLINDER_SKY_VAR_FOG_BEG_PERCENT, 0, 0.f, 1.f, 0.01f, 2));
		s_VarMan.AddVar(new FloatVariable("Fog End Percent", CYLINDER_SKY_VAR_FOG_END_PERCENT, 0, 0.f, 1.f, 0.01f, 2));
		s_VarMan.AddVar(new Uint32Variable("Layer Count", CYLINDER_SKY_VAR_LAYER_COUNT, 0, 1, 4, 1));
				
		s_VarMan.FinishInit();
	}
	return s_VarMan;
}

CylinderSky::CylinderSky(const CylinderSkyDesc &desc)
: FogColor(COLORF_BLACK)
, FogBegPercent(0.5f)
, FogEndPercent(1.0f)
, m_Shader(NULL)
, m_LayerCount(1)
, m_VB(NULL)
, m_IB(NULL)
, m_SegmentCount(desc.SegmentCount)
, m_VertexCount(0), m_TriangleCount(0)
{
	for (uint li = 0; li < _countof(TexTransforms_C); li++)
	{
		TexTransforms_C[li] = MATRIX_IDENTITY;
		TexTransforms_T[li] = MATRIX_ZERO;
		m_Textures[li] = NULL;
		BlendModes[li] = BLEND_ADD;
		Colors[li] = COLORF_WHITE;
	}

	string path, cachePath;
	RelativeToAbsolutePath(&path, frame::DataDir, "Shaders\\Sky_Cylinder.fx");
	RelativeToAbsolutePath(&cachePath, frame::DataDir, "Cache\\Sky_Cylinder*.fxo");
	m_Shader = Multishader::GetOrCreate(
		"Sky_Cylinder.fx",
		path, cachePath,
		SKY_CYLINDER_SHADER_MACRO_NAMES,
		SKY_CYLINDER_SHADER_MACRO_BIT_COUNTS,
		_countof(SKY_CYLINDER_SHADER_MACRO_NAMES));

	CreateBuffers(desc.SegmentCount, desc.Axes);
}

CylinderSky::~CylinderSky()
{
	for (uint li = 4; li--; )
	{
		SAFE_RELEASE(m_Textures[li]);
	}

	SAFE_RELEASE(m_IB);
	SAFE_RELEASE(m_VB);
	SAFE_RELEASE(m_Shader);
}

bool CylinderSky::Render( const ParamsCamera &camera )
{
	if (m_Shader == NULL)
		return false;

	if (renderer::g_DiffuseTexturesEnabled)
	{
		for (uint li = 0; li < m_LayerCount; li++)
		{
			if (m_Textures[li] == NULL)
				return false;
			if (!m_Textures[li]->IsLoaded())
			{
				m_Textures[li]->BeginLoading();
				return false;
			}
		}
	}

	uint macroValues[] = {
		m_LayerCount >= 1 ? (BlendModes[1] == BLEND_LERP ? 2 : 1) : 0,
		m_LayerCount >= 2 ? (BlendModes[2] == BLEND_LERP ? 2 : 1) : 0,
		m_LayerCount >= 3 ? (BlendModes[3] == BLEND_LERP ? 2 : 1) : 0,
	};

	ID3DXEffect *effect = m_Shader->GetEffect(macroValues, Multishader::FLAG_BEGIN_LOADING);
	if (effect == NULL)
		return false;

	float t = frame::Timer2.GetTime().ToSeconds_f();

	const uint segCount = 16;
	float fogDenom = FogEndPercent-FogBegPercent;
	assert(!FLOAT_ALMOST_ZERO(fogDenom));
	fogDenom = 1.f / fogDenom;
	VEC4 fogParams = VEC4(fogDenom, -FogBegPercent*fogDenom, 0.f, 1.f);

	if (renderer::g_Wireframe)
		renderer::RenderSingleColorSky(camera, COLORF_BLACK);

	frame::Dev->SetRenderState(D3DRS_ZENABLE, TRUE);
	frame::Dev->SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
	frame::Dev->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
	frame::Dev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
	frame::Dev->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
	frame::Dev->SetRenderState(D3DRS_STENCILENABLE, FALSE);
	frame::Dev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	frame::Dev->SetRenderState(D3DRS_FILLMODE, renderer::g_Wireframe ? D3DFILL_WIREFRAME : D3DFILL_SOLID);
	frame::Dev->SetFVF(VERTEX_X2::FVF);
	frame::Dev->SetStreamSource(0, m_VB, 0, sizeof(VERTEX_X2));
	frame::Dev->SetIndices(m_IB);
	
	MATRIX worldViewProj = camera.GetMatrices().GetView();
	worldViewProj._41 = worldViewProj._42 = worldViewProj._43 = 0.f; // Clear camera translation.
	worldViewProj *= camera.GetMatrices().GetProj();

	D3DVIEWPORT9 oldViewport, newViewport;
	frame::Dev->GetViewport(&oldViewport);
	newViewport = oldViewport;
	newViewport.MinZ = newViewport.MaxZ = 1.f;
	frame::Dev->SetViewport(&newViewport);

	ERR_GUARD_DIRECTX( effect->SetMatrix("g_WorldViewProj", &worldViewProj) );
	ERR_GUARD_DIRECTX( effect->SetVector("g_FogParams", &fogParams) );
	ERR_GUARD_DIRECTX( effect->SetVector("g_FogColor", &VEC4(FogColor.R, FogColor.G, FogColor.B, FogColor.A)) );

	IDirect3DBaseTexture9 *textures[4];
	if (renderer::g_DiffuseTexturesEnabled)
	{
		for (uint li = 0; li < m_LayerCount; li++)
			textures[li] = m_Textures[li]->GetBaseTexture();
	}
	else
	{
		for (uint li = 0; li < m_LayerCount; li++)
			textures[li] = GetEmptyDiffuseTexture();
	}

	// No breaks here.
	switch (m_LayerCount)
	{
	case 4:
		ERR_GUARD_DIRECTX( effect->SetMatrix("g_TexTransform3", &(TexTransforms_C[3] + t*TexTransforms_T[3])) );
		ERR_GUARD_DIRECTX( effect->SetTexture("g_Texture3", textures[3]) );
		ERR_GUARD_DIRECTX( effect->SetVector("g_Color3", &ColorfToVec4(Colors[3])) );
	case 3:
		ERR_GUARD_DIRECTX( effect->SetMatrix("g_TexTransform2", &(TexTransforms_C[2] + t*TexTransforms_T[2])) );
		ERR_GUARD_DIRECTX( effect->SetTexture("g_Texture2", textures[2]) );
		ERR_GUARD_DIRECTX( effect->SetVector("g_Color2", &ColorfToVec4(Colors[2])) );
	case 2:
		ERR_GUARD_DIRECTX( effect->SetMatrix("g_TexTransform1", &(TexTransforms_C[1] + t*TexTransforms_T[1])) );
		ERR_GUARD_DIRECTX( effect->SetTexture("g_Texture1", textures[1]) );
		ERR_GUARD_DIRECTX( effect->SetVector("g_Color1", &ColorfToVec4(Colors[1])) );
	default:
		ERR_GUARD_DIRECTX( effect->SetMatrix("g_TexTransform0", &(TexTransforms_C[0] + t*TexTransforms_T[0])) );
		ERR_GUARD_DIRECTX( effect->SetTexture("g_Texture0", textures[0]) );
		ERR_GUARD_DIRECTX( effect->SetVector("g_Color0", &ColorfToVec4(Colors[0])) );
	}

	UINT passCount;
	ERR_GUARD_DIRECTX( effect->Begin(&passCount, 0) );
	ERR_GUARD_DIRECTX( effect->BeginPass(0) );

	const VEC2 &screenSize = frame::GetBackBufferSize();


	frame::Dev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, m_VertexCount, 0, m_TriangleCount);
	frame::RegisterDrawCall(m_TriangleCount);

	ERR_GUARD_DIRECTX( effect->EndPass() );
	ERR_GUARD_DIRECTX( effect->End() );

	frame::Dev->SetViewport(&oldViewport);

	return true;
}

void CylinderSky::GetParamValue(uint id, void *outData) const
{
	switch (id)
	{
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_0_C, MATRIX, TexTransforms_C[0])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_0_T, MATRIX, TexTransforms_T[0])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_1_C, MATRIX, TexTransforms_C[1])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_1_T, MATRIX, TexTransforms_T[1])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_2_C, MATRIX, TexTransforms_C[2])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_2_T, MATRIX, TexTransforms_T[2])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_3_C, MATRIX, TexTransforms_C[3])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_3_T, MATRIX, TexTransforms_T[3])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_BLEND_MODE_1, uint, BlendModes[1])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_BLEND_MODE_2, uint, BlendModes[2])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_BLEND_MODE_3, uint, BlendModes[3])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_FOG_COLOR, COLORF, FogColor)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_FOG_BEG_PERCENT, float, FogBegPercent)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_FOG_END_PERCENT, float, FogEndPercent)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_LAYER_COUNT, uint, GetLayerCount())
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEXTURE_0, string, m_Textures[0] ? m_Textures[0]->GetFileName() : EMPTY_STRING)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEXTURE_1, string, m_Textures[1] ? m_Textures[1]->GetFileName() : EMPTY_STRING)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEXTURE_2, string, m_Textures[2] ? m_Textures[2]->GetFileName() : EMPTY_STRING)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_TEXTURE_3, string, m_Textures[3] ? m_Textures[3]->GetFileName() : EMPTY_STRING)
	GET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR0, COLORF, Colors[0])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR1, COLORF, Colors[1])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR2, COLORF, Colors[2])
	GET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR3, COLORF, Colors[3])
	default: assert(0);
	}
}

void CylinderSky::SetParamValue(uint id, const void *inData)
{
	switch (id)
	{
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_0_C, MATRIX, TexTransforms_C[0])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_0_T, MATRIX, TexTransforms_T[0])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_1_C, MATRIX, TexTransforms_C[1])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_1_T, MATRIX, TexTransforms_T[1])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_2_C, MATRIX, TexTransforms_C[2])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_2_T, MATRIX, TexTransforms_T[2])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_3_C, MATRIX, TexTransforms_C[3])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_TEX_TRANSFORM_3_T, MATRIX, TexTransforms_T[3])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_BLEND_MODE_1, uint, (uint&)BlendModes[1])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_BLEND_MODE_2, uint, (uint&)BlendModes[2])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_BLEND_MODE_3, uint, (uint&)BlendModes[3])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_FOG_COLOR, COLORF, FogColor)
	SET_PARAM_VAL(CYLINDER_SKY_VAR_FOG_BEG_PERCENT, float, FogBegPercent)
	SET_PARAM_VAL(CYLINDER_SKY_VAR_FOG_END_PERCENT, float, FogEndPercent)
	SET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR0, COLORF, Colors[0])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR1, COLORF, Colors[1])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR2, COLORF, Colors[2])
	SET_PARAM_VAL(CYLINDER_SKY_VAR_COLOR3, COLORF, Colors[3])
	
	case CYLINDER_SKY_VAR_LAYER_COUNT:
		SetLayerCount(*(const uint*)inData);
		break;
	case CYLINDER_SKY_VAR_TEXTURE_0:
	case CYLINDER_SKY_VAR_TEXTURE_1:
	case CYLINDER_SKY_VAR_TEXTURE_2:
	case CYLINDER_SKY_VAR_TEXTURE_3:
		{
			uint texIndex = id - CYLINDER_SKY_VAR_TEXTURE_0;
			const string &filePath = *(const string*)inData;
			if (filePath.empty())
				SetTexture(texIndex, NULL);
			else
				SetTexture(texIndex, D3dTextureFromFile::GetOrCreate(filePath, filePath));
		}
		break;
	default:
		assert(0);
	}
}

void CylinderSky::SetLayerCount( uint v )
{
	assert(v > 0 && v <= 4);
	m_LayerCount = v;
}

void CylinderSky::SetTexture( uint layer, D3dTextureFromFile *texture )
{
	UpdateObjPtr(m_Textures[layer], texture);
}

void CylinderSky::CreateBuffers( uint segmentCount, const VEC3 axes[3] )
{
	m_VertexCount = (segmentCount+1)*2;
	const uint cylinderQuadCount = segmentCount * 2;
	const uint capTriCount = segmentCount - 2;
	m_TriangleCount = cylinderQuadCount*2 + capTriCount*2;

	ERR_GUARD_DIRECTX( frame::Dev->CreateVertexBuffer(m_VertexCount*sizeof(VERTEX_X2), 0, VERTEX_X2::FVF, D3DPOOL_MANAGED, &m_VB, NULL) );
	ERR_GUARD_DIRECTX( frame::Dev->CreateIndexBuffer(m_TriangleCount*3*sizeof(uint2), 0, D3DFMT_INDEX16, D3DPOOL_MANAGED, &m_IB, NULL) );

	{
		VertexBufferLock vbLock(m_VB, 0);
		VERTEX_X2 *vb = (VERTEX_X2*)vbLock.GetData();

		float f = 0.f, fStep = PI_X_2 / segmentCount;
		float texX = 0.f, texXStep = 1.f / segmentCount;
		float x, y;
		VEC3 tmpPos, zVec = axes[2];
		for (uint i = 0, vi = 0; i <= segmentCount; i++, f += fStep, texX += texXStep)
		{
			sincos(f, &x, &y);
			tmpPos = axes[0] * x + axes[1] * y;
			vb[vi].Pos = tmpPos - zVec;
			vb[vi].Tex.x = texX;
			vb[vi].Tex.y = 0.f;
			vi++;
			vb[vi].Pos = tmpPos + zVec;
			vb[vi].Tex.x = texX;
			vb[vi].Tex.y = 1.f;
			vi++;
		}
	}

	{
		IndexBufferLock ibLock(m_IB, 0);
		uint2 *ib = (uint2*)ibLock.GetData();
		uint ii = 0;
		// Cylinder
		{
			uint2 vi = 0;
			for (uint qi = 0; qi < segmentCount; qi++)
			{
				ib[ii++] = vi;
				ib[ii++] = vi+1;
				ib[ii++] = vi+2;
				ib[ii++] = vi+2;
				ib[ii++] = vi+1;
				ib[ii++] = vi+3;
				vi += 2;
			}
		}
		// Caps
		{
			uint viBeg = 1, viEnd = segmentCount;
			for (uint cap_i = 0; cap_i < capTriCount; cap_i++)
			{
				if ((cap_i & 1) == 0)
				{
					ib[ii++] = (viBeg  )*2;
					ib[ii++] = (viEnd  )*2;
					ib[ii++] = (viEnd-1)*2;

					ib[ii++] = (viBeg  )*2+1;
					ib[ii++] = (viEnd  )*2+1;
					ib[ii++] = (viEnd-1)*2+1;

					viEnd--;
				}
				else
				{
					ib[ii++] = (viBeg  )*2;
					ib[ii++] = (viEnd  )*2;
					ib[ii++] = (viBeg+1)*2;

					ib[ii++] = (viBeg  )*2+1;
					ib[ii++] = (viEnd  )*2+1;
					ib[ii++] = (viBeg+1)*2+1;

					viBeg++;
				}
			}
		}
	}
}
